package com.ruyiadmin.springboot.controller.system;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruyiadmin.springboot.common.annotations.system.Log;
import com.ruyiadmin.springboot.common.annotations.system.Permission;
import com.ruyiadmin.springboot.common.beans.system.QuartzConfig;
import com.ruyiadmin.springboot.common.beans.system.SystemCacheConfig;
import com.ruyiadmin.springboot.common.classes.system.ScheduleJobMessage;
import com.ruyiadmin.springboot.common.components.core.RuYiRedisComponent;
import com.ruyiadmin.springboot.common.components.core.RuYiSessionContext;
import com.ruyiadmin.springboot.common.constants.business.JobAction;
import com.ruyiadmin.springboot.common.core.business.enums.JobStatus;
import com.ruyiadmin.springboot.common.core.business.enums.OperationType;
import com.ruyiadmin.springboot.common.core.system.entities.ActionResult;
import com.ruyiadmin.springboot.common.core.system.entities.QueryCondition;
import com.ruyiadmin.springboot.common.core.system.entities.QueryResult;
import com.ruyiadmin.springboot.common.exceptions.RuYiAdminCustomException;
import com.ruyiadmin.springboot.domain.dto.system.SysUserDTO;
import com.ruyiadmin.springboot.domain.entity.system.SysScheduleJob;
import com.ruyiadmin.springboot.service.iservices.system.ISysScheduleJobService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.quartz.CronExpression;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * <p>
 * 计划任务表 前端控制器
 * </p>
 *
 * @author RuYiAdmin
 * @since 2022-07-12
 */
@RestController
@RequestMapping("/ScheduleJobManagement")
@Api(tags = "系统计划任务管理服务")
@RequiredArgsConstructor
public class SysScheduleJobController {

    //region 服务私有属性

    private final ISysScheduleJobService scheduleJobService;
    private final RuYiRedisComponent redisUtils;
    private final SystemCacheConfig systemCacheConfig;
    private final QuartzConfig quartzConfig;
    private final RuYiSessionContext sessionContext;

    //endregion

    //region 查询任务列表

    @PostMapping("/Post")
    @ApiOperation(value = "查询任务列表")
    @Log(OperationType = OperationType.QueryList)
    @Permission(permission = "job:query:list")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public QueryResult<SysScheduleJob> queryByPage(@RequestBody QueryCondition queryCondition)
            throws ExecutionException, InterruptedException {
        CompletableFuture<QueryResult<SysScheduleJob>> future = CompletableFuture.supplyAsync(() -> {
            QueryWrapper<SysScheduleJob> wrapper = new QueryWrapper<>();//设置条件
            queryCondition.getQueryWrapper(wrapper);//转化查询条件、转化排序条件
            Page<SysScheduleJob> page = new Page<>(queryCondition.getPageIndex(),
                    queryCondition.getPageSize());//初始化page

            this.scheduleJobService.page(page, wrapper);//执行查询
            long total = page.getTotal();//总数
            List<SysScheduleJob> rs = page.getRecords();//结果

            return QueryResult.success(total, rs);
        });
        return future.get();
    }

    //endregion

    //region 查询任务信息

    @GetMapping("/GetById/{jobId}")
    @ApiOperation(value = "查询任务信息")
    @Log(OperationType = OperationType.QueryEntity)
    @Permission(permission = "job:query:list")
    public ActionResult getById(@PathVariable("jobId") String jobId)
            throws ExecutionException, InterruptedException {
        CompletableFuture<ActionResult> future = CompletableFuture.supplyAsync(() -> {
            Object value = this.redisUtils.get(systemCacheConfig.getScheduleJobCacheName());
            List<SysScheduleJob> scheduleJobs = JSON.parseArray(value.toString(), SysScheduleJob.class);
            SysScheduleJob sysScheduleJob = scheduleJobs.stream()
                    .filter(t -> t.getId().equals(jobId))
                    .collect(Collectors.toList())
                    .get(0);
            return ActionResult.success(sysScheduleJob);
        });
        return future.get();
    }

    //endregion

    //region 新增计划任务

    @PostMapping("/Add")
    @ApiOperation(value = "新增计划任务")
    @Log(OperationType = OperationType.AddEntity)
    @Permission(permission = "job:add:entity")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult add(@Valid @RequestBody SysScheduleJob scheduleJob) {
        //校验CronExpression表达式
        boolean expression = CronExpression.isValidExpression(scheduleJob.getCronExpression());
        if (!expression) {
            throw new RuYiAdminCustomException("invalid cron expression");
        }
        scheduleJob.setJobStatus(JobStatus.Planning.ordinal());
        //支持集群作业
        if (this.quartzConfig.isSupportGroup()) {
            //保存为本节点定时任务
            scheduleJob.setGroupId(this.quartzConfig.getGroupId());
        }
        //保存计划任务
        this.scheduleJobService.save(scheduleJob);

        //region 数据一致性维护

        //获取缓存数据
        Object value = this.redisUtils.get(systemCacheConfig.getScheduleJobCacheName());
        List<SysScheduleJob> scheduleJobs = JSON.parseArray(value.toString(), SysScheduleJob.class);

        //添加新数据
        scheduleJobs.add(scheduleJob);
        //更新计划任务缓存
        this.redisUtils.set(systemCacheConfig.getScheduleJobCacheName(), JSON.toJSONString(scheduleJobs));

        //endregion

        return ActionResult.ok();
    }

    //endregion

    //region 编辑计划任务

    @PutMapping("/Put")
    @ApiOperation(value = "编辑计划任务")
    @Log(OperationType = OperationType.EditEntity)
    @Permission(permission = "job:edit:entity")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult edit(@Valid @RequestBody SysScheduleJob scheduleJob) {
        //校验CronExpression表达式
        boolean expression = CronExpression.isValidExpression(scheduleJob.getCronExpression());
        if (!expression) {
            throw new RuYiAdminCustomException("invalid cron expression");
        }

        Object value = this.redisUtils.get(systemCacheConfig.getScheduleJobCacheName());
        List<SysScheduleJob> scheduleJobs = JSON.parseArray(value.toString(), SysScheduleJob.class);

        SysScheduleJob job = scheduleJobs.stream().
                filter(t -> t.getId().equals(scheduleJob.getId())).
                collect(Collectors.toList()).get(0);

        if (job.getJobStatus() == JobStatus.Running.ordinal()) {
            throw new RuYiAdminCustomException("job is running,please stop it first");
        }

        //更新计划任务
        this.scheduleJobService.updateById(scheduleJob);

        //region 数据一致性维护

        //删除旧数据
        for (Iterator<SysScheduleJob> iterator = scheduleJobs.iterator(); iterator.hasNext(); ) {
            SysScheduleJob element = iterator.next();
            if (element.getId().equals(scheduleJob.getId())) {
                iterator.remove();
                break;
            }
        }

        //添加新数据
        scheduleJobs.add(scheduleJob);

        //更新计划任务缓存
        this.redisUtils.set(systemCacheConfig.getScheduleJobCacheName(), JSON.toJSONString(scheduleJobs));

        //endregion

        return ActionResult.ok();
    }

    //endregion

    //region 删除计划任务

    @DeleteMapping("/Delete/{jobId}")
    @ApiOperation(value = "删除计划任务")
    @Log(OperationType = OperationType.DeleteEntity)
    @Permission(permission = "job:del:entity")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult delete(@PathVariable("jobId") String jobId) throws Exception {
        Object value = this.redisUtils.get(systemCacheConfig.getScheduleJobCacheName());
        List<SysScheduleJob> scheduleJobs = JSON.parseArray(value.toString(), SysScheduleJob.class);

        SysScheduleJob job = scheduleJobs.stream().
                filter(t -> t.getId().equals(jobId)).
                collect(Collectors.toList()).get(0);

        if (!this.quartzConfig.isSupportGroup()) {
            //单机版，不支持集群，删除
            this.scheduleJobService.deleteScheduleJob(jobId, StringUtils.EMPTY);

            //region 数据一致性维护

            //删除旧数据
            for (Iterator<SysScheduleJob> iterator = scheduleJobs.iterator(); iterator.hasNext(); ) {
                SysScheduleJob element = iterator.next();
                if (element.getId().equals(job.getId())) {
                    iterator.remove();
                    break;
                }
            }

            //更新计划任务缓存
            this.redisUtils.set(systemCacheConfig.getScheduleJobCacheName(), JSON.toJSONString(scheduleJobs));

            //endregion

        } else {
            if (job.getGroupId() > 0 && job.getGroupId().equals(this.quartzConfig.getGroupId())) {
                //支持集群、且为本节点任务，删除
                this.scheduleJobService.deleteScheduleJob(jobId, StringUtils.EMPTY);

                //region 数据一致性维护

                //删除旧数据
                for (Iterator<SysScheduleJob> iterator = scheduleJobs.iterator(); iterator.hasNext(); ) {
                    SysScheduleJob element = iterator.next();
                    if (element.getId().equals(job.getId())) {
                        iterator.remove();
                        break;
                    }
                }

                //更新计划任务缓存
                this.redisUtils.set(systemCacheConfig.getScheduleJobCacheName(), JSON.toJSONString(scheduleJobs));

                //endregion

            } else {

                //region 发布分布式任务调度消息

                ScheduleJobMessage message = new ScheduleJobMessage();
                message.setJobId(jobId);
                message.setGroupId(job.getGroupId());
                message.setAction(JobAction.Delete);
                SysUserDTO user = sessionContext.getCurrentUserInfo();
                message.setUserId(user.getId());

                String msg = JSON.toJSONString(message);
                this.redisUtils.convertAndSend(this.quartzConfig.getChanelName(), msg);

                //endregion

            }
        }
        return ActionResult.ok();
    }

    //endregion

    //region 启动计划任务

    @GetMapping("/StartScheduleJob/{jobId}")
    @ApiOperation(value = "启动计划任务")
    @Log(OperationType = OperationType.StartScheduleJob)
    @Permission(permission = "schedule:job:add")
    public ActionResult startScheduleJob(@PathVariable("jobId") String jobId) throws Exception {
        Object value = this.redisUtils.get(systemCacheConfig.getScheduleJobCacheName());
        List<SysScheduleJob> scheduleJobs = JSON.parseArray(value.toString(), SysScheduleJob.class);

        SysScheduleJob job = scheduleJobs.stream()
                .filter(t -> t.getId().equals(jobId))
                .collect(Collectors.toList())
                .get(0);

        if (!this.quartzConfig.isSupportGroup()) {
            //单机版，不支持集群
            this.scheduleJobService.startScheduleJob(jobId, StringUtils.EMPTY);
        } else {
            if (job.getGroupId() > 0 && job.getGroupId().equals(this.quartzConfig.getGroupId())) {
                //支持集群、且为本节点任务
                this.scheduleJobService.startScheduleJob(jobId, StringUtils.EMPTY);
            } else {
                //region 发布分布式任务调度消息

                ScheduleJobMessage message = new ScheduleJobMessage();
                message.setJobId(jobId);
                message.setGroupId(job.getGroupId());
                message.setAction(JobAction.Start);
                SysUserDTO user = sessionContext.getCurrentUserInfo();
                message.setUserId(user.getId());

                String msg = JSON.toJSONString(message);
                this.redisUtils.convertAndSend(this.quartzConfig.getChanelName(), msg);

                //endregion
            }
        }

        return ActionResult.ok();
    }

    //endregion

    //region 暂停计划任务

    @GetMapping("/PauseScheduleJob/{jobId}")
    @ApiOperation(value = "暂停计划任务")
    @Log(OperationType = OperationType.PauseScheduleJob)
    @Permission(permission = "schedule:job:pause")
    public ActionResult pauseScheduleJob(@PathVariable("jobId") String jobId) throws Exception {
        Object value = this.redisUtils.get(systemCacheConfig.getScheduleJobCacheName());
        List<SysScheduleJob> scheduleJobs = JSON.parseArray(value.toString(), SysScheduleJob.class);

        SysScheduleJob job = scheduleJobs.stream()
                .filter(t -> t.getId().equals(jobId))
                .collect(Collectors.toList())
                .get(0);

        if (!this.quartzConfig.isSupportGroup()) {
            //单机版，不支持集群
            this.scheduleJobService.pauseScheduleJob(jobId, StringUtils.EMPTY);
        } else {
            if (job.getGroupId() > 0 && job.getGroupId().equals(this.quartzConfig.getGroupId())) {
                //支持集群、且为本节点任务
                this.scheduleJobService.pauseScheduleJob(jobId, StringUtils.EMPTY);
            } else {
                //region 发布分布式任务调度消息

                ScheduleJobMessage message = new ScheduleJobMessage();
                message.setJobId(jobId);
                message.setGroupId(job.getGroupId());
                message.setAction(JobAction.Pause);
                SysUserDTO user = sessionContext.getCurrentUserInfo();
                message.setUserId(user.getId());

                String msg = JSON.toJSONString(message);
                this.redisUtils.convertAndSend(this.quartzConfig.getChanelName(), msg);

                //endregion
            }
        }

        return ActionResult.ok();
    }

    //endregion

    //region 恢复计划任务

    @GetMapping("/ResumeScheduleJob/{jobId}")
    @ApiOperation(value = "恢复计划任务")
    @Log(OperationType = OperationType.ResumeScheduleJob)
    @Permission(permission = "schedule:job:resume")
    public ActionResult resumeScheduleJob(@PathVariable("jobId") String jobId) throws Exception {
        Object value = this.redisUtils.get(systemCacheConfig.getScheduleJobCacheName());
        List<SysScheduleJob> scheduleJobs = JSON.parseArray(value.toString(), SysScheduleJob.class);

        SysScheduleJob job = scheduleJobs.stream()
                .filter(t -> t.getId().equals(jobId))
                .collect(Collectors.toList())
                .get(0);

        if (!this.quartzConfig.isSupportGroup()) {
            //单机版，不支持集群
            this.scheduleJobService.resumeScheduleJob(jobId, StringUtils.EMPTY);
        } else {
            if (job.getGroupId() > 0 && job.getGroupId().equals(this.quartzConfig.getGroupId())) {
                //支持集群、且为本节点任务
                this.scheduleJobService.resumeScheduleJob(jobId, StringUtils.EMPTY);
            } else {
                //region 发布分布式任务调度消息

                ScheduleJobMessage message = new ScheduleJobMessage();
                message.setJobId(jobId);
                message.setGroupId(job.getGroupId());
                message.setAction(JobAction.Resume);
                SysUserDTO user = sessionContext.getCurrentUserInfo();
                message.setUserId(user.getId());

                String msg = JSON.toJSONString(message);
                this.redisUtils.convertAndSend(this.quartzConfig.getChanelName(), msg);

                //endregion
            }
        }

        return ActionResult.ok();
    }

    //endregion

}
